//
// Created by haoy on 2017/3/10.
//

#ifndef RECORD_PACKET_REORDER_HPP
#define RECORD_PACKET_REORDER_HPP

#include <cassert>
#include <cstdint>
#include <list>
#include <map>
#include <vector>

#include "dbg.hpp"

template <class Packet>
class PacketReorder {
public:
    PacketReorder() : _max_packets(5120), _head_seq(0), _tail_seq(0) {

    }

    PacketReorder(int max) : _max_packets(max), _head_seq(0), _tail_seq(0) {

    }

    bool add(std::vector<Packet*>& overflow, Packet* packet) {
        uint16_t seq = packet->GetSeqNum();
        uint16_t udelta = seq - _tail_seq;
        uint16_t next_head = _head_seq;
        uint16_t next_tail = _tail_seq;

        if (udelta < 3000 || (_head_seq == 0 && _tail_seq == 0) ) {
            // advance tail if:
            // - seq is ahead of tail less than 3000
            // - head and tail was not set
            next_tail = seq;
            next_head = seq - _max_packets + 1;
        } else {
            // drop it if seq is ahead of head more than max packets
            // if (seq - _head_seq >= _max_packets ) {
            //     dbgi("reorder: ssrc %u seq %lu out of window [%u,%u], dropped\n", packet->GetSSRC(), seq, _head_seq, _tail_seq);
            //     return false;
            // }
            //ERROR : reorder: ssrc 2679857867 seq 4989 out of window [65513,5096], dropped
            if ((_head_seq >= _tail_seq && (seq > _tail_seq && seq < _head_seq))
            || (_head_seq < _tail_seq && ( (seq >= 0 && seq < _head_seq) || (seq > _tail_seq && seq <= 65535) ))) {
                dbgi("reorder: ssrc %u seq %u out of window [%u,%u], dropped\n", packet->GetSSRC(), seq, _head_seq, _tail_seq);
                return false;
            }
        }

        if (_packets.count(seq)) {
            dbgd("reorder: ssrc %u seq %u duplicated, dropped\n", packet->GetSSRC(), packet->GetExtSeqNum());
            return false;
        }
        // move old packets to overflow
        move(overflow, _head_seq, next_head);
        // put new packet
        _packets[seq] = packet;
        // update head and tail
        _head_seq = next_head;
        _tail_seq = next_tail;
        return true;
    }

    void reset(std::vector<Packet*>& remaining_packets) {
        move(remaining_packets, _head_seq, _tail_seq + 1);
        _head_seq = 0;
        _tail_seq = 0;

        if (_packets.size()) {
            dbge("packet leak detected:");
            dump_packets();
        }
        return;
    }

    void dump_packets() {
        dbge("---- leaked packets ----");
        for (auto p : _packets) {
            dbge("packet %u", p.second->GetSeqNum());
        }
        dbge("---- end of leaked packets ----");
    }
private:
    void move(std::vector<Packet *> &overflow, uint16_t from, uint16_t until) {
        if (from < until) {
            for (uint16_t i = from; i < until; ++i) {
                // skip packets seq absent
                if (_packets.count(i) == 0) {
                    continue;
                }
                //dbgi("reorder: ssrc %u seq %lu ptr %X dequeue\n", _packets[i]->GetSSRC(), _packets[i]->GetExtSeqNum(), _packets[i]);
                overflow.push_back(_packets[i]);
                _packets.erase(i);
            }
        } else if (from > until) {
            for (uint16_t i = from; i <= 65535; ++i) {
                if (i == 0) {
                    break;
                }
                // skip packets seq absent
                if (_packets.count(i) == 0) {
                    continue;
                }
                //dbgi("reorder: ssrc %u seq %lu ptr %X dequeue\n", _packets[i]->GetSSRC(), _packets[i]->GetExtSeqNum(), _packets[i]);
                overflow.push_back(_packets[i]);
                _packets.erase(i);
            }
            for (uint16_t i = 0; i < until; ++i) {
                // skip packets seq absent
                if (_packets.count(i) == 0) {
                    continue;
                }
                //dbgi("reorder: ssrc %u seq %lu ptr %X dequeue\n", _packets[i]->GetSSRC(), _packets[i]->GetExtSeqNum(), _packets[i]);
                overflow.push_back(_packets[i]);
                _packets.erase(i);
            }
        }
    }

private:
    size_t _max_packets;
    uint16_t _head_seq;
    uint16_t _tail_seq;
    std::map<size_t, Packet*> _packets;
};


#endif //RECORD_PACKET_REORDER_HPP
